# -*- coding:utf-8 -*-
from __future__ import absolute_import
import json
import datetime
from urllib import parse
import requests

from huaweicloudsdkcore.auth.credentials import BasicCredentials
from huaweicloudsdkcore.exceptions import exceptions
from huaweicloudsdksmn.v2.smn_client import SmnClient
from huaweicloudsdksmn.v2.region.smn_region import SmnRegion
from huaweicloudsdksmn.v2.model.publish_message_request import PublishMessageRequest
from huaweicloudsdksmn.v2.model.publish_message_request_body import PublishMessageRequestBody

# event格式参考
'''
{
    'Records': [
        {
            'eventVersion': '2.0',
            'eventSource': 'aws:s3',
            'awsRegion': 'cn-south-1',
            'eventTime': '2021-12-25T08:02:30.389Z',
            'eventName': 'ObjectCreated:Put',
            'userIdentity': {'principalId': '879c657349c44a9c9ac345867ea999d1'},
            'requestParameters': {'sourceIPAddress': '10.48.51.83'},
            'responseElements': {'x-amz-request-id': '35a9e0041b7a4bff8903365011f81fe2',
                              'x-amz-id-2': 'FJZA3Hq0hNm8ZQAECeAjlglzI4iBNSG/Y347hRAIjtlzR4AnHjs/JfEy1+bOi8tE'},
            's3': {
                's3SchemaVersion': '1.0',
                'configurationId': 'obs-event-18t4',
                'bucket': {
                    'name': 'autopolit',
                    'ownerIdentity': {
                        'PrincipalId': '8c1d78bc75bf4154a4d0c73a28c3e0b5'

                    },
                    'arn': 'arn:aws:s3:::autopolit'
                },
                'object': {
                    'key': 'setup.py', #'dir1%2F__init__.py'
                    'eTag': 'a6e683a165f9443a3224a010923cd240',
                    'size': 1149,
                    'versionId': 'null',
                    'sequencer': '0000000017DF09CD77FD0596C0000000'
                }
            }
         }
    ]
}
'''
logger = None


class S3Object:
    def __init__(self, event):
        self.region = ""
        self.collect_time = ""
        self.bucket_name = ""
        self.object_name = ""
        self.size = ""
        self.path = None
        self.file_name = None
        self.status = False
        self.error_msg = ""
        self.get_value_from_event(event=event)

    def get_value_from_event(self, event):
        try:
            self.region = event['Records'][0]['awsRegion']
            self.collect_time = event['Records'][0]['eventTime']
            # Obtains a bucket name.
            self.bucket_name = parse.unquote(event['Records'][0]['s3']['bucket']["name"])
            # Obtains the name of an uploaded object.
            self.object_name = parse.unquote(event['Records'][0]['s3']['object']["key"])
            self.size = int(event['Records'][0]['s3']['object']["size"])
            self.path = "obs://{}/{}".format(self.bucket_name, self.object_name)
            self.file_name = self.object_name.split("/")[-1]
            self.status = True
        except Exception as e:
            global logger
            logger.error("invalid event, {}".format(str(e)))
            self.error_msg = str(e)
            self.status = False


def smn_publish_message(smn_client, topic_urn, msg):
    try:
        request = PublishMessageRequest()
        request.topic_urn = topic_urn
        request.body = PublishMessageRequestBody(
            message=msg,
            subject="场景提取任务通知"
        )
        response = smn_client.publish_message(request)
        logger.info(response)
    except exceptions.ClientRequestException as e:
        logger.error(e.status_code)
        logger.error(e.request_id)
        logger.error(e.error_code)
        logger.error(e.error_msg)


class APIClient:
    def __init__(self):
        self.error_msg = ""
        self.ploto_username = None
        self.ploto_password = None
        self.api_ploto_login = None
        self.api_ploto_anonymized_add = None
        self.api_bags_add_isv = None
        self.api_bags_scene_cut = None
        self.scene_output_obs = None
        self.topic_urn = None
        self.topic_region = None

        self.s3_object = None

        self.bag_id_isv = None
        self.scene_cut_task_id = None

    def get_values(self, event, context):
        context_status = True  # 用来判断配置正确性
        error_msg = ""
        self.ploto_username = context.getUserData('plotoUsername')
        self.ploto_password = context.getUserData('plotoPassword')
        if self.ploto_username is None or self.ploto_password is None:
            context_status = False
            error_msg = "{}{}\n".format(error_msg,
                                        "invalid context, not got ploto user information(plotoUsername/plotoPassword)")

        self.api_ploto_login = context.getUserData('apiPlotoLogin')
        self.api_ploto_anonymized_add = context.getUserData('apiPlotoAdd')
        if self.api_ploto_login is None or self.api_ploto_anonymized_add is None:
            context_status = False
            error_msg = "{}{}\n".format(error_msg,
                                        "invalid context, not got ploto api information(apiPlotoLogin/apiPlotoAdd)")

        self.api_bags_add_isv = context.getUserData('apiIsvAdd')
        self.api_bags_scene_cut = context.getUserData('apiIsvSceneCut')
        if self.api_bags_add_isv is None or self.api_bags_scene_cut is None:
            context_status = False
            error_msg = "{}{}\n".format(error_msg,
                                        "invalid context, not got isv api information(apiIsvAdd/apiIsvSceneCut)")

        self.topic_urn = context.getUserData('topicUrn')  # 消息通知的主题，可从官网console页面查找
        if self.topic_urn is not None:
            topic_split = self.topic_urn.split(":")
            if len(topic_split) != 5:
                context_status = False
                error_msg = "{}{}\n".format(error_msg, "invalid context, Parameter: topicUrn.")
            self.topic_region = topic_split[2]

        self.s3_object = S3Object(event)
        if not self.s3_object.status:
            context_status = False
            error_msg = "{}{}\n".format(error_msg, "invalid event, {}".format(self.s3_object.error_msg))

        self.error_msg = error_msg
        return context_status

    def add_bags_isv(self):
        date_str = datetime.datetime.today().strftime("%Y%m%d%H%M%S")
        bag_remark = "add from obs event.create time is {}".format(date_str)

        post_body = {"name": self.s3_object.file_name, "path": self.s3_object.path, "remarks": bag_remark}
        headers = {"Content-Type": "application/json"}
        response = requests.post(url=self.api_bags_add_isv, data=json.dumps(post_body), headers=headers)
        rep_json = json.loads(response.text)  # {"errno": 0, "errmsg":"OK","data":{"bag_id":38},"details":None}

        if rep_json.get("errno", None) == 0:
            self.bag_id_isv = rep_json.get("data", {}).get("bag_id", None)
            return True
        else:
            self.error_msg = "isv bags add failed.errmsg:{},details:{}".format(rep_json.get("errmsg", ""),
                                                                               rep_json.get("details", ""))
            logger.error(self.error_msg)
            return False

    def add_bags_ploto(self):
        headers = {"Content-Type": "application/json"}
        data_login = {"username": self.ploto_username, "password": self.ploto_password}
        response = requests.post(url=self.api_ploto_login, data=json.dumps(data_login), headers=headers)
        if response.status_code != 200:
            self.error_msg = "ploto login failed.errmsg:{}".format(response.text)
            logger.error(self.error_msg)
            return False

        rep_json = json.loads(response.text)
        csrf_token = rep_json.get("CSRFToken", None)
        headers["X-CSRFToken"] = csrf_token
        data_add = {"name": self.s3_object.file_name, "size": self.s3_object.size, "data_type": 0, "storage_type": 0,
                    "collect_time": datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S"),
                    "url": self.s3_object.path, "source_type": "fgs_holomatic", "source_id": self.bag_id_isv,
                    "task_id": self.scene_cut_task_id}
        response_add = requests.post(url=self.api_ploto_anonymized_add, data=json.dumps(data_add), headers=headers,
                                     cookies=response.cookies)
        if response_add.status_code == 200:
            return True
        else:
            self.error_msg = "ploto anonymized data add failed.errmsg:{}".format(response_add.text)
            logger.error(self.error_msg)
            return False

    def scene_cut_isv(self):
        obj_path = "/".join(self.s3_object.object_name.split("/")[:-1])
        obs_output_dir = "obs://{}/{}".format(self.scene_output_obs, obj_path)

        # 可指定需要提取的场景，默认全提取
        # body 体中增加  "scenes": "follow_big_car,cut_in,overtake,confluence,follow_and_stop"
        data = {"bag_id": self.bag_id_isv, "bag_path": self.s3_object.path, "obs_output_dir": obs_output_dir}
        headers = {"Content-Type": "application/json"}
        rep = requests.post(url=self.api_bags_scene_cut, data=json.dumps(data), headers=headers)
        rep_json = json.loads(rep.text)
        if rep.status_code == 200 and rep_json.get("errno") == 0:
            logger.info("isv bags cut scene success.")
            self.scene_cut_task_id = rep_json.get("task_id")
            return True
        else:
            self.error_msg = rep_json.get("errmsg")
            logger.error(self.error_msg)
            return False


def handler(event, context):
    global logger
    logger = context.getLogger()
    ak = context.getAccessKey()
    sk = context.getSecretKey()

    api_client = APIClient()
    if not api_client.get_values(event, context):
        raise Exception(api_client.error_msg)

    if api_client.s3_object.size == 0:
        # size = 0 表示该对象为目录，无需进行后续操作
        logger.warning("invalid object, size was 0.")
        return "OK"

    date_str = datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")
    bag_path = "obs://{}/{}".format("s3_object.bucket_name", "s3_object.object_name")
    message_template = "尊敬的华为云用户：\n" \
                       "您于" + date_str + "上传的数据" + bag_path + "，调用数据平台{task_type}任务{status}，请关注。\n" \
                                                              "详细信息：{errmsg}"

    credentials = BasicCredentials(ak, sk)
    smn_smn_client = SmnClient.new_builder() \
        .with_credentials(credentials) \
        .with_region(SmnRegion.value_of(api_client.topic_region)) \
        .build()

    if not api_client.add_bags_isv():
        message = message_template.format(task_type="新增", status="失败", errmsg=api_client.error_msg)
        smn_publish_message(smn_smn_client, api_client.topic_urn, message)
        raise Exception(api_client.error_msg)

    if not api_client.scene_cut_isv():
        message = message_template.format(task_type="场景提取", status="失败", errmsg=api_client.error_msg)
        smn_publish_message(smn_smn_client, api_client.topic_urn, message)
        raise Exception(api_client.error_msg)

    if not api_client.add_bags_ploto():
        message = message_template.format(task_type="新增", status="失败", errmsg=api_client.error_msg)
        smn_publish_message(smn_smn_client, api_client.topic_urn, message)
        raise Exception(api_client.error_msg)

    message = message_template.format(task_type="脱敏数据新增和场景提取任务", status="成功", errmsg=api_client.error_msg)
    smn_publish_message(smn_smn_client, api_client.topic_urn, message)
    return "OK"
